﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Emerson.Storage.Service.Interface;
using Emerson.Storage.Entities;
using System.Data.Objects;
using System.Data;
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels;
using Emerson.Storage.Agent.Core;
using System.Runtime.Remoting;
using System.Configuration;
using System.Threading;
using System.Threading.Tasks;
using System.Net.Sockets;
using System.Collections.Concurrent;

namespace Emerson.Storage.Service
{
    public class UpdateManager
    {
        private ManualResetEvent reqestStop = new ManualResetEvent(false);
        public UpdateManager()
        {
        }

        public void StartUpdating()
        {
            Thread updateThread = new Thread(new ParameterizedThreadStart(UpdateLoop));
            updateThread.Name = "UpdateThread";
            updateThread.IsBackground = true;
            updateThread.Start();
        }

        private void UpdateLoop(object state)
        {
            while (true)
            {
                //  UpdateRoutine();
                UpdateRoutineUsingTasks();
                Thread.Sleep(3 * 1000);
            }
        }

        private void UpdateRoutineUsingTasks()
        {
            using (SmartStorageEntities sse = new SmartStorageEntities())
            {
                List<Workstation> workstationList;
                var cnt = sse.Group.Count();
                workstationList = sse.Workstation.ToList();
                int workstationCnt = workstationList.Count;
                if (workstationCnt == 0)
                    return;
                foreach (var wk in workstationList)
                    wk.Battery.Load();

                var cachedWorkstations = new ConcurrentDictionary<int, WorkstationDataPack>();
                var updateActions = new Action[workstationCnt];

                for (int i = 0; i < workstationCnt; i++)
                {
                    Workstation curStation = workstationList[i];
                    updateActions[i] = () =>
                    {
                        cachedWorkstations.TryAdd(curStation.WorkstationId, QueryWorkstation(curStation.IPAddress));
                    };
                }

                //now fire those updating actions, current thread would be blocked.
                Parallel.Invoke(updateActions);

                //now unpack the workstation data and fill the real data.
                FillWorkstationInfo(workstationList, cachedWorkstations);

                //Apply rules here

                //Save updated workstations back
                try
                {
                    sse.SaveChanges();
                }
                catch (OptimisticConcurrencyException)
                {
                    //Refresh the database, compromise to the store data
                    foreach (Workstation wk in workstationList)
                    {
                        sse.Refresh(RefreshMode.StoreWins, wk.Battery);
                    }
                    sse.Refresh(RefreshMode.StoreWins, workstationList);

                    sse.SaveChanges();
                }
            }
        }

        private void FillWorkstationInfo(List<Workstation> workstationList, ConcurrentDictionary<int, WorkstationDataPack> cachedWorkstations)
        {
            foreach (var curStation in workstationList)
            {
                WorkstationDataPack data;
                bool succeed = cachedWorkstations.TryGetValue(curStation.WorkstationId, out data);
                if (succeed && data.IsConnected)
                {
                    curStation.UpdateWorkstationData(data.stationInfo);
                    curStation.IsConnected = true;
                    foreach (var bat in curStation.Battery)
                    {
                        BatteryInfo bi = data.batteryInfos.First((b) => b.BatteryType == bat.BatteryType);
                        bat.UpdateBatteryData(bi);
                    }
                }
                else
                {
                    curStation.IsConnected = false;
                }
            }
        }

        private WorkstationDataPack QueryWorkstation(string ipAddress)
        {
            IAgentService agentService = AgentAccess.GetAgentService(ipAddress.Trim());
            WorkstationDataPack data = new WorkstationDataPack();
            data.IsConnected = false;

            try
            {
                List<BatteryInfo> bis = agentService.GetBatteryInfos();
                WorkstationInfo wi = agentService.GetWorkstationInfo();
                data.stationInfo = wi;
                data.batteryInfos = bis;
                data.IsConnected = true;
            }
            catch (SocketException se)
            {
                string msg = se.Message;
                data.IsConnected = false;
            }

            return data;
        }

        private void UpdateRoutine()
        {
            List<Workstation> workstationList;


            using (SmartStorageEntities sse = new SmartStorageEntities())
            {
                var cnt = sse.Group.Count();
                workstationList = sse.Workstation.ToList();
                if (workstationList.Count == 0)
                    return;

                foreach (var wk in workstationList)
                    wk.Battery.Load();

                List<WorkstationUpdater> updaters = new List<WorkstationUpdater>();
                List<ManualResetEvent> updaterCompletedEvents = new List<ManualResetEvent>();

                foreach (var workstation in workstationList)
                {
                    ManualResetEvent completedEvent = new ManualResetEvent(false);
                    WorkstationUpdater updater = new WorkstationUpdater(workstation, completedEvent);
                    updater.DoUpdate();
                    updaters.Add(updater);
                    updaterCompletedEvents.Add(completedEvent);
                }


                //wait for all updaters to complete within 2 minutes
                //calling thread will be blocked
                WaitHandle.WaitAll(updaterCompletedEvents.ToArray(), new TimeSpan(0, 2, 0));

                //Apply rules here

                //Save updated workstations back
                try
                {
                    sse.SaveChanges();
                }
                catch (OptimisticConcurrencyException)
                {
                    //Refresh the database, compromise to the store data
                    foreach (Workstation wk in workstationList)
                    {
                        sse.Refresh(RefreshMode.StoreWins, wk.Battery);
                    }
                    sse.Refresh(RefreshMode.StoreWins, workstationList);

                    sse.SaveChanges();
                }
            }
        }

        /// <summary>
        /// StopUpdating is sync method, so it will block the calling thread untill the updating loop exits
        /// </summary>
        public void StopUpdating()
        {
            // updateTimer.Change(Timeout.Infinite, 0);
        }
    }
}
